• 幂等性

    幂等性即为不管进行多少次重复操作,或者说一次和多次请求某一个资源应该具有相同的副作用。

  • REST请求中的幂等性操作

    GET、PUT和DELETE都是幂等操作,而POST不是。

    • GET方法用于获取资源,不应有副作用,所以是幂等的,需要注意的是,这里强调的是一次和多次具有相同的副作用,而不是多次GET请求的结果相同
    • DELETE方法用于删除资源,第一次将资源删除后,后面多次进行此删除操作,最终结果都是一样的
    • PUT请求的幂等性可以这样理解,将A改成B,第一次请求值变成了B,再进行多次此操作,最终的结果还是B,与一次执行的结果是一样的
    • POST方法会在服务器端创建资源,两次相同的POST请求会在服务器端产生两份资源,它们具有不同的URI,因此,POST请求不具有幂等性
  • 分布式事务与幂等性

    看下面的例子,假设有一个从账户取钱的远程API,我们暂时用函数的方式记为:

    1
    bool withdraw(account_id, amount);

    withdraw的语义是从account_id对应的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount;如果扣除失败则返回false,账户余额不变。值得注意的是:和本地环境相比,我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了withdraw被调用两次,账户也被多扣了一次钱。

    这个问题的解决方案一是采用分布式事务,通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单,复杂性都交给了中间件来管理。缺点则是一方面架构太重量级,容易被绑在特定的中间件上,不利于异构系统的集成;另一方面分布式事务虽然能保证事务的ACID性质,而但却无法提供性能和可用性的保证。

    另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把withdraw变成幂等的,比如:

    1
    2
    int create_ticket()
    bool idempotent_withdraw(ticket_id, account_id, amount)

    create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id,它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id,一个ticket_id表示的操作至多只会被处理一次,每次调用都将返回第一次调用时的处理结果。这样,idempotent_withdraw就符合幂等性了,客户端就可以放心地多次调用。

    基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤:1.调用create_ticket()获取ticket_id;2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的,但在这种设计下,它对系统状态的影响可以忽略,加上idempotent_withdraw是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。

  • 根据幂等性区分POST和PUT的使用

    举一个简单的例子,假如有一个博客系统提供一个Web API,模式是这样http://superblogging/blogs/{blog-name},很简单,将{blog-name}替换为我们的blog名字,往这个URI发送一个HTTP PUT或者POST请求,HTTP的body部分就是博文,这是一个很简单的REST API例子。

    我们应该用PUT方法还是POST方法?

    取决于这个REST服务的行为是否是idempotent的,假如我们发送两个http://superblogging/blogs/post/Sample请求,服务器端是什么样的行为?如果产生了两个博客帖子,那就说明这个服务不是idempotent的,因为多次使用产生了副作用了嘛;如果后一个请求把第一个请求覆盖掉了,那这个服务就是idempotent的。前一种情况,应该使用POST方法,后一种情况,应该使用PUT方法。

  • 本文转自利用幂等性区分HTTP的POST与PUT请求HTTP幂等性及GET、POST、PUT、DELETE的区别